package co.edu.unal.bioingenium.kbmed.knowledge.mapping.main;

import co.edu.unal.bioingenium.kbmed.config.Configuration;
import co.edu.unal.bioingenium.kbmed.knowledge.KnowledgeService;
import co.edu.unal.bioingenium.kbmed.knowledge.KnowledgeSourceMetaData;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAO;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAOFactory;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.InformationContentService;
import co.edu.unal.bioingenium.kbmed.knowledge.weirdness.WeirdnessService;
import co.edu.unal.bioingenium.kbmed.text.filter.FilterPipe;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexStrToStr;
import co.edu.unal.bioingenium.kbmed.text.index.structures.InvertedIndexStrToWordInContext;
import co.edu.unal.bioingenium.kbmed.text.index.io.InvertedIndexIO;
import co.edu.unal.bioingenium.kbmed.text.representation.vo.WordList;
import co.edu.unal.bioingenium.kbmed.text.representation.io.WordListIO;
import ds.tree.RadixTreeImpl;
import ds.tree.io.PrefixIndexIO;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author lariverosc
 */
public class IndexKnowledSourceMain {

    private boolean fby_weirdness = false;
    private float MIN_WEIRDNESS = 100;
    private boolean fby_concept_ic = false;
    private float MIN_IC = 0.3f;
    private boolean fby_concept_level = false;
    private int MAX_CONCEPT_LEVEL = 8;
    private FilterPipe pipeline;
    private boolean fby_term_tokens = false;
    private int TERM_MAX_TOKENS = 6;
    private KnowledgeSourceDAO knowledgeSourceDAO;
    private KnowledgeService knowledgeService;
    private InformationContentService informationContentService;
    private WeirdnessService weirdnessService;

    /**
     * 
     */
    public IndexKnowledSourceMain() {
        knowledgeSourceDAO = KnowledgeSourceDAOFactory.getKnowledgeSourceDAO();
        pipeline = new FilterPipe();
        pipeline.setMinTokenLength(3);
        pipeline.setRemoveNumbers(true);
        knowledgeService = KnowledgeService.getInstance();
        informationContentService = InformationContentService.getInstance();
        weirdnessService = WeirdnessService.getInstance();

    }

    /**
     * @param:
     * @return:
     */
    public void buildIndexByConcepts() {
        try {
            InvertedIndexStrToStr wordByConceptIndex = new InvertedIndexStrToStr();
            WordList wordList = new WordList(Configuration.KNOWLEDGE_SOURCE_NAME);
            System.out.println("Indexing by concepts " + KnowledgeSourceMetaData.getReference().getName());
            Set<String> conceptsIds = knowledgeSourceDAO.getConceptsIds();
            int totalConcepts = conceptsIds.size();
            Map<String, String> conceptDescriptions;
            int i = 0;
            for (String conceptId : conceptsIds) {//For each concept
                System.out.println((++i) + "/" + totalConcepts + " indexing " + conceptId);
                conceptDescriptions = knowledgeSourceDAO.getTermsForConcept(conceptId);
                for (String descriptionId : conceptDescriptions.keySet()) {//For each descriptor associated with the concept
                    processTerm(wordList, wordByConceptIndex, descriptionId, conceptDescriptions.get(descriptionId));
                }
            }
            wordByConceptIndex.pruneAbove(2000);
            System.out.println(wordList.getNumWords() + " words in " + KnowledgeSourceMetaData.getReference().getName());
            InvertedIndexIO.storeStrToStrInvertedIndex(wordByConceptIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByConcept.idx");
            WordListIO.storeWordList(wordList, Configuration.KNOWLEDGE_SOURCE_NAME + "ConceptsWords.lst");
            RadixTreeImpl<Integer> prefixIndex = buildPrefixIndex(wordList.getSortedWordList());
            PrefixIndexIO.store(prefixIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "ConceptsWordsPrefix.idx");
        } catch (IOException ex) {
            Logger.getLogger(IndexKnowledSourceMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * @param:
     * @return:
     */
    public void buildIndexByDescriptors() {
        try {
            InvertedIndexStrToStr wordByDescriptionIndex = new InvertedIndexStrToStr();
            WordList wordList = new WordList(Configuration.KNOWLEDGE_SOURCE_NAME);
            System.out.println("Indexing by descriptors " + KnowledgeSourceMetaData.getReference().getName());
            Set<String> conceptsIds = knowledgeSourceDAO.getConceptsIds();
            int totalConcepts = conceptsIds.size();
            Map<String, String> descriptions;
            int i = 0;
            for (String conceptId : conceptsIds) {//For each concept
                System.out.println((++i) + "/" + totalConcepts + " indexing " + conceptId);
                descriptions = knowledgeSourceDAO.getTermsForConcept(conceptId);
                for (String descriptorId : descriptions.keySet()) { //For each descriptor associated with the concept
                    processTerm(wordList, wordByDescriptionIndex, descriptorId, descriptions.get(descriptorId));
                }
            }
            System.out.println(wordList.getNumWords() + " words in " + KnowledgeSourceMetaData.getReference().getName());
            InvertedIndexIO.storeStrToStrInvertedIndex(wordByDescriptionIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByDescriptor.idx");
            WordListIO.storeWordList(wordList, Configuration.KNOWLEDGE_SOURCE_NAME + "DescriptorsWords.lst");
            RadixTreeImpl<Integer> prefixIndex = buildPrefixIndex(wordList.getSortedWordList());
            PrefixIndexIO.store(prefixIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "DescriptorsWordsPrefix.idx");
        } catch (IOException ex) {
            Logger.getLogger(IndexKnowledSourceMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    private List<String> tempWords;

    private void processTerm(WordList wordList, InvertedIndexStrToStr wordByDescriptionIndex, String descriptorId, String term) {
        String conceptId = knowledgeService.getConceptIdForDescriptorId(descriptorId);
        String category = knowledgeService.getCategoryForConceptId(conceptId);

        if (fby_concept_ic) {
            if (informationContentService.getInformationContentValue(category, conceptId) < MIN_IC) {
                return;
            }
        }
        if (fby_concept_level) {
            if (knowledgeService.getDeepInHierarchyByConceptId(conceptId) > MAX_CONCEPT_LEVEL) {
                return;
            }
        }
        tempWords = pipeline.filter(term);
        if (fby_term_tokens) {
            if (tempWords.size() > TERM_MAX_TOKENS) {
                return;
            }
        }
        for (String word : tempWords) {//For each word in the descriptor
            if (fby_weirdness) {
                if (weirdnessService.getWeirdness(word) < MIN_WEIRDNESS) {
                    continue;
                }
            }
            //To index
            wordByDescriptionIndex.addElement(word, descriptorId); //Add word-descriptor pair to index
            //To wordlist
            wordList.addWord(word); //Add word to WordList
            //To counts
            wordList.addGlobalOcurrence(word);// Add word global ocurrence
            if (!wordByDescriptionIndex.getElement(word).contains(descriptorId)) {
                wordList.addInObjectOccurence(word); // Add in object occurrence
            }
        }
    }

    /**
     * @param:
     * @return:     */
    public void buildIndexByConceptsWithContext() {
        try {
            InvertedIndexStrToWordInContext wordByConceptContextIndex = new InvertedIndexStrToWordInContext();
            WordList wordList = new WordList(KnowledgeSourceMetaData.getReference().getName());
            System.out.println("Indexing by concepts with context" + KnowledgeSourceMetaData.getReference().getName());
            Set<String> conceptsIds = knowledgeSourceDAO.getConceptsIds();
            int totalConcepts = conceptsIds.size();
            Map<String, String> conceptDescriptions;
            int i = 0;
            for (String conceptId : conceptsIds) {//For each concept
                System.out.println((++i) + "/" + totalConcepts + " indexing " + conceptId);
                conceptDescriptions = knowledgeSourceDAO.getTermsForConcept(conceptId);
                for (String descriptionId : conceptDescriptions.keySet()) {//For each descriptor associated with the concept
                    processTermWithContext(wordList, wordByConceptContextIndex, conceptId, conceptDescriptions.get(descriptionId));
                }
            }
            System.out.println(wordList.getNumWords() + " words in " + KnowledgeSourceMetaData.getReference().getName());
            InvertedIndexIO.storeStrToWordInContextInvertedIndex(wordByConceptContextIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByConceptCtx.idx");
            WordListIO.storeWordList(wordList, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByConcept.lst");


        } catch (IOException ex) {
            Logger.getLogger(IndexKnowledSourceMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * @param:
     * @return:
    
     */
    public void buildIndexByDescriptorsWithContext() {
        try {
            InvertedIndexStrToWordInContext wordByDescriptionContextIndex = new InvertedIndexStrToWordInContext();
            WordList wordList = new WordList(KnowledgeSourceMetaData.getReference().getName());
            System.out.println("Indexing by descriptors with context" + KnowledgeSourceMetaData.getReference().getName());
            Set<String> conceptsIds = knowledgeSourceDAO.getConceptsIds();
            int totalConcepts = conceptsIds.size();
            Map<String, String> descriptions;
            int i = 0;
            for (String conceptId : conceptsIds) {//For each concept
                System.out.println((++i) + "/" + totalConcepts + " indexing " + conceptId);
                descriptions = knowledgeSourceDAO.getTermsForConcept(conceptId);
                for (String descriptionId : descriptions.keySet()) { //For each descriptor associated with the concept
                    processTermWithContext(wordList, wordByDescriptionContextIndex, conceptId, descriptions.get(descriptionId));
                }
            }
            System.out.println(wordList.getNumWords() + " words in " + KnowledgeSourceMetaData.getReference().getName());
            InvertedIndexIO.storeStrToWordInContextInvertedIndex(wordByDescriptionContextIndex, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByDescriptorCtx.idx");
            WordListIO.storeWordList(wordList, Configuration.KNOWLEDGE_SOURCE_NAME + "WordByDescriptor.lst");


        } catch (IOException ex) {
            Logger.getLogger(IndexKnowledSourceMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void processTermWithContext(WordList wordList, InvertedIndexStrToWordInContext wordByConceptContextIndex, String conceptId, String term) {
        tempWords = pipeline.filter(term);
        if (tempWords.size() <= TERM_MAX_TOKENS) {
            short context = 0;
            for (String word : tempWords) {//For each word in the descriptor
                //To index
                wordByConceptContextIndex.addElement(word, conceptId, context); //Add word-concept pair to index
                //To wordlist
                wordList.addWord(word); //Add word to WordList
                //To counts
                wordList.addGlobalOcurrence(word);// Add word global ocurrence
                if (!wordByConceptContextIndex.getElement(word).containsKey(conceptId)) {
                    wordList.addInObjectOccurence(word);// Add in object occurrence
                }
                context++;
            }
        }
    }

    /**
     * @param:
     * @param name 
     * @return:
     * @param words 
     */
    private RadixTreeImpl<Integer> buildPrefixIndex(List<String> words) {
        System.out.println("Creating the prefix tree for all words.");
        RadixTreeImpl<Integer> prefixIndex = new RadixTreeImpl<Integer>();
        Integer count = 0;
        String subString;
        for (String word : words) {
            if (word.length() >= 4) {
                subString = word.substring(0, 4);
                if (!prefixIndex.contains(subString)) {
                    prefixIndex.insert(subString, count);
                }
            } else {
                if (!prefixIndex.contains(word)) {
                    prefixIndex.insert(word, count);
                }
            }
            count++;
        }
        return prefixIndex;
    }

    /**
     * @param:
     * @return:
     * @param args 
     */
    public static void main(String... args) {
        IndexKnowledSourceMain indexKnowledSource = new IndexKnowledSourceMain();
        indexKnowledSource.buildIndexByDescriptors();
        indexKnowledSource.buildIndexByConcepts();
        indexKnowledSource.buildIndexByDescriptorsWithContext();
        indexKnowledSource.buildIndexByConceptsWithContext();
    }
}
